﻿/*
 * RenderTransformMatrix
 * 
 * initial implementation : 20.10.2008
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using Petra.Gis.Geometry.Support;

namespace Petra.Gis.RenderingServices.PlanarBase 
{
  /// <summary>
  /// Base abstract class for map display/print/render transformation described as matrix.
  /// Projected (world) coordinate is transformed by given parameters (offset, scale, rotation)
  /// to display on map in small local coordinate system (pixels, points,...)
  /// this class supports transformation of <c>CoordStruct</c> and can return <c>System.Drawing.Point</c>
  /// </summary>
  public abstract class RenderTransformMatrix
  {

    protected double[] _matrixArray;


    #region constructors

    protected RenderTransformMatrix()
    {

      _matrixArray = new double[9];

      _matrixArray[0] = 1;
      _matrixArray[1] = 0;
      _matrixArray[2] = 0;

      _matrixArray[3] = 0;
      _matrixArray[4] = 1;
      _matrixArray[5] = 0;

      _matrixArray[6] = 0;
      _matrixArray[7] = 0;
      _matrixArray[8] = 1;
    }



    protected RenderTransformMatrix(float m11, float m12, float m21, float m22, float dx, float dy)
      : this()
    {
      _matrixArray[0] = m11;
      _matrixArray[1] = m12;
      
      _matrixArray[3] = m21;
      _matrixArray[4] = m22;

      _matrixArray[6] = dx;
      _matrixArray[7] = dy;
    }


    #endregion constructors


    #region public members


    /// <summary>
    /// set/get offset (shift) transform value in direction of X axis of output map coordinate system
    /// </summary>
    public double OffsetX
    {
      get { return _matrixArray[6]; }
      set { _matrixArray[6] = value; }
    }



    /// <summary>
    /// set/get offset (shift) transform value in direction of Y axis of output map coordinate system
    /// </summary>
    public double OffsetY
    {
      get { return _matrixArray[7]; }
      set { _matrixArray[7] = value; }
    }



    /// <summary>
    /// set/get scale transform value in direction of X axis of output map coordinate system
    /// </summary>
    public double ScaleX
    {
      get { return _matrixArray[0]; }
      set { _matrixArray[0] = value; }
    }



    /// <summary>
    /// set/get scale transform value in direction of Y axis of output map coordinate system
    /// </summary>
    public double ScaleY
    {
      get { return _matrixArray[4]; }
      set { _matrixArray[4] = value; }
    }



    /// <summary>
    /// get rotation of output map. Currently rotation is unsupported and this property returns 0 value.
    /// </summary>
    public double Rotation
    {
      get { return 0.0; }
      //set { _matrixArray[4] = value; }
    }




    /// <summary>
    /// Applies the specified translation vector (offsetX and offsetY) to 
    /// this DisplayTransformMatrix by prepending the translation vector.
    /// </summary>
    /// <param name="offsetX">The horizontal value by which to translate this DisplayTransformMatrix.</param>
    /// <param name="offsetY">The vertical value by which to translate this DisplayTransformMatrix.</param>
    public void Translate(double offsetX, double offsetY)
    {
      _matrixArray[6] = offsetX;
      _matrixArray[7] = offsetY;
    }





    /// <summary>
    /// Applies the specified scale vector to this DisplayTransformMatrix by prepending the scale vector.
    /// </summary>
    /// <param name="scaleX">The value by which to scale this DisplayTransformMatrix in the horizontal direction</param>
    /// <param name="scaleY">The value by which to scale this DisplayTransformMatrix in the vertical direction</param>
    public void Scale(float scaleX, float scaleY)
    {
      _matrixArray[0] = scaleX;
      _matrixArray[4] = scaleY;
    }





    /// <summary>
    /// Applies the specified scale vector to this DisplayTransformMatrix by prepending the scale vector.
    /// </summary>
    /// <param name="scaleX">The value by which to scale this DisplayTransformMatrix in the horizontal direction</param>
    /// <param name="scaleY">The value by which to scale this DisplayTransformMatrix in the vertical direction</param>
    public void Scale(double scaleX, double scaleY)
    {
      _matrixArray[0] = scaleX;
      _matrixArray[4] = scaleY;
    }




    /// <summary>
    /// Prepend to this DisplayTransformMatrix a clockwise rotation,
    /// around the origin and by the specified angle.
    /// Current version doesnt support rotation, input value is ignored
    /// </summary>
    /// <param name="angle">The angle of the rotation, in degrees.</param>
    public void Rotate(float angle)
    {
      //not implemented 
    }





    /// <summary>
    /// helper function that provides scale transform for scalar value by taking X-scale factor from matrix
    /// </summary>
    /// <param name="scalarValue"></param>
    /// <returns></returns>
    public virtual int TransformScalarByX(double scalarValue)
    {
      //can use X

      scalarValue *= _matrixArray[4];

      return (int)scalarValue;

    }




    /// <summary>
    /// Transforms input coordinate given by 2 separate scalar values into Projected (world) coordinates.
    /// This is useful when handling mouse events on map window.
    /// </summary>
    /// <param name="X">X coordinate in map window</param>
    /// <param name="Y">Y coordinate in map window</param>
    /// <returns><c>Coordinate</c> in Projected (world) coordinate system</returns>
    public virtual Coordinate MapToProjection(float X, float Y)
    {
      Coordinate outPt = new Coordinate(0, 0, 0);

      // must keep this order
      // 1. scale
      outPt.X = X / _matrixArray[0];
      outPt.Y = Y / _matrixArray[4];

      // 2. translate
      outPt.X += _matrixArray[6];
      outPt.Y += _matrixArray[7];

      //// 3. flip never forget this !!!
      //if (_flipAxes)
      //{
      //  float tmp = X;
      //  X = Y;
      //  Y = tmp;
      //}

      return outPt;
    }



    /// <summary>
    /// transform matrix element indexer
    /// </summary>
    /// <param name="row">valid matrix row index &lt;0,2&gt;</param>
    /// <param name="column">valid matrix row index &lt;0,2&gt;</param>
    /// <returns>matrix transform value for specified 2d, 3x3 matrix index</returns>
    public double this[int row, int column]
    {
      get
      {
        if (row < 0 || row > 2 || column < 0 || column > 2)
          throw new IndexOutOfRangeException();

        return _matrixArray[(row * 3) + column];
      }
    }





    //protected bool _flipAxes = false;

    /// <summary>
    /// helper property. In some cases is usefull to define transform as simple axis orientation flip.
    /// Not implemented in current version.
    /// </summary>
    public bool FlipAxes
    { get; set; }





    /// <summary>
    /// helper property. In some cases is usefull to define transform as simple axis orientation flip.
    /// Not implemented in current version
    /// </summary>
    public bool SwapAxes
    { get; set; }




    //public Point TransformPoint(CoordStruct inputPoint)
    //{
    //  Point outPt = new Point();

    //  //translate
    //  inputPoint.X -= _matrixArray[6];
    //  inputPoint.Y -= _matrixArray[7];

    //  //scale
    //  inputPoint.X *= _matrixArray[0];
    //  inputPoint.Y *= _matrixArray[4];

    //  outPt.X = (int)inputPoint.X;
    //  outPt.Y = (int)inputPoint.Y;

    //  return outPt;
    //}




    //public Point transformPoint(PointF point)
    //{

    //  CoordStruct inputPoint = new CoordStruct(point.X, point.Y);

    //  return TransformPoint(inputPoint);

    //}




    //public Point TransformPoint(Coordinate coord)
    //{

    //  CoordStruct inputPoint = new CoordStruct(coord.X, coord.Y);

    //  return TransformPoint(inputPoint);
    //}




    //public Point TransformPoint(double horizontal, double vertical)
    //{

    //  CoordStruct inputPoint = new CoordStruct(horizontal, vertical);

    //  return TransformPoint(inputPoint);
    //}





    //public double[] Parameters
    //{
    //  get { return _matrixArray; }
    //  set 
    //  {
    //    if(value.Length != 9)
    //      throw new ArgumentException("invalid input array, must be double[9]");
    //    _matrixArray = value;
    //  }
    //}




    #endregion public members


    #region protected members


    //protected abstract object projectionToMap(Coordinate inputPoint);

    //public abstract object ProjectionToMap(double X, double Y);

    //protected abstract Coordinate mapToProjection(object inputPoint);


    #endregion protected members


  }
}
